package groep1;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import bluej.debugger.DebuggerField;
import bluej.debugger.DebuggerObject;
import bluej.debugmgr.inspector.FieldInfo;
import bluej.debugmgr.inspector.Inspector;

@Kuleuven
public class ObjectRepresentation {
	
	protected DebuggerObject debuggerObject;
	protected String name;
	protected List<FieldInfo> fieldList;
	protected String modifier = "";

	/**Velden van objecten die naar hem verwijzen*/
	protected HashMap<ObjectRepresentation,List<DebuggerField>> fieldReferences = new HashMap<ObjectRepresentation,List<DebuggerField>>();
	/**Velden van object die naar andere objecten wijzen*/
//	private HashMap<DebuggerField, DebuggerObject> fieldReferers = new HashMap<DebuggerField, DebuggerObject>();

	protected boolean inObjectBench;
	
	public ObjectRepresentation(DebuggerObject object, boolean inObjectBench){
		debuggerObject = object;
		this.inObjectBench = inObjectBench;
	}
	
	/**
	 * Returns a unique key for this ObjectRepresentation instance.
	 * 
	 * @return	a unique key for this ObjectRepresentation instance.
	 */
	public String getUniqueKey() {
		return debuggerObject.getObjectReference().uniqueID()+":"+debuggerObject.getClassName().toString()+"."+getName();
	}
	
	/**
	 * Adds an object that refers to this object in one of his fields.
	 * 
	 * @param 	fieldName
	 * 			| The fieldName of the field that is referring.
	 * @param 	object
	 * 			| The object that refers to this object.
	 */
	public void addReference(DebuggerField field, ObjectRepresentation object){
		if(fieldReferences.containsKey(debuggerObject)){
			List<DebuggerField> fields = fieldReferences.get(debuggerObject);
			fields.add(field);
		}
		else{
			ArrayList<DebuggerField> newList = new ArrayList<DebuggerField>();
			newList.add(field);
			fieldReferences.put(object, newList);
		}
	}
	
	/**
	 * Adds an object to witch this object is referring to in one of his field.
	 * 
	 * @param 	fieldName
	 * 			| The fieldname of this object that refers to the other object.
	 * @param 	object
	 * 			| The other object to with this object refers.
	 */
//	public void addReferers(DebuggerField field, DebuggerObject object){
//		fieldReferers.put(field, object);
//	}
	
	/**
	 * Returns the debuggerObject of the object.
	 * 
	 * @return The debuggerObject of the object.
	 */
	public DebuggerObject getObject() {
		return debuggerObject;
	}
	
	
	/**
	 * Returns a FieldList with the fields of the debuggerObject this object
	 * is a representation of.
	 * 
	 * @return The FieldList with the fields of the debuggerObject this object
	 * 			is a representation of.
	 */
	public void buildFieldList(boolean toString) {
		DebuggerObject obj = getObject();
		List<FieldInfo> fieldInfoList = new ArrayList<FieldInfo>();
		List<DebuggerField> lijst = obj.getFields();
		for (DebuggerField debuggerField : lijst){
			ObjectRepresentation objectrepresentation = ObjectList.getInstance().getObjectRepresentation(debuggerField.getValueObject(null));
			if(objectrepresentation != null) {
				if (objectrepresentation.getObject().isArray() && toString) {
					fieldInfoList.add(new FieldInfo(Inspector.fieldToString(debuggerField),
							objectrepresentation.getArrayString()));
				}
				else
					fieldInfoList.add(new FieldInfo(Inspector.fieldToString(debuggerField),
							objectrepresentation.getUniqueKey()));
				
			} else
				fieldInfoList.add(new FieldInfo(Inspector.fieldToString(debuggerField),
					debuggerField.getValueString()));
		}
		fieldList = fieldInfoList;
	}
	
	/**
	 * 
	 * @return
	 */
	private String getArrayString() {
		String ret = "{";
		for (FieldInfo fieldInfo : fieldList) {
			ret += fieldInfo.getValue()+",";
		}
		if (ret.endsWith(","))
			ret.substring(0,ret.lastIndexOf(",")-1);
		ret += "}";
		return ret;
	}

	/**
	 * Returns a FieldList with the fields of the debuggerObject this array object
	 * is a representation of.
	 * 
	 * @return The FieldList with the fields of the debuggerObject this array object
	 * 			is a representation of.
	 */
	public void buildArrayFieldList() {
		DebuggerObject obj = getObject();
		List<FieldInfo> fieldInfoList = new ArrayList<FieldInfo>();
		String type = obj.getElementType().toString();
		for (int i = 0; i < obj.getElementCount(); i ++) {
			String value = obj.getElementValueString(i);
			fieldInfoList.add(new FieldInfo(modifier+type+" "+i, value));
		}
		fieldList = fieldInfoList;
	}
	
	public void setModifier(DebuggerField debuggerField) {
		int mods = debuggerField.getModifiers();
	    if (Modifier.isPrivate(mods)) {
	        modifier = "private ";
	    } else if (Modifier.isPublic(mods)) {
	        modifier = "public ";
	    } else if (Modifier.isProtected(mods)) {
	        modifier = "protected ";
	    }
	}
	
	/**
	 * Returns a list of all the fields of the debuggerObject this instance is
	 * a representation of.
	 * 
	 * @return	a list of all the fields of the debuggerObject this instance is
	 * 			a representation of.
	 */
	public List<FieldInfo> getFieldList(){
		return fieldList;
	}
	
	/**
	 * Returns if the object is in the objectBench.
	 * 
	 * @return true if object in objectBench.
	 */
	public boolean isInBench(){
		return inObjectBench;
	}
	
	/**
	 * Change indicates that the object is on the objectBench.
	 */
	public void setOnBench(){
		inObjectBench = true;
	}
	
	/**
	 * Prints the fieldReferences.
	 */
	public void printFieldReferences() {
		for(ObjectRepresentation object: fieldReferences.keySet()){
			for(DebuggerField field:fieldReferences.get(object)){
				System.out.println(field.getName()+": "+object.getObject().getClassName());
			}
		}
	}

	/**
	 * Prints the fieldReferers.
	 */
//	public void printFieldReferers() {
//		for(DebuggerField field: fieldReferers.keySet()){
//			DebuggerObject object = fieldReferers.get(field);
//			System.out.println(field.getName()+": "+object.getClassName());
//		}
//	}
	
	/**
	 * Return the name of the instance this object is a representation of.
	 * 
	 * @return the name of the instance this object is a representation of.
	 */
	public String getName() {
		return name;
	}

	/**
	 * Set the name of the instance this object is a representation of.
	 * 
	 * @param	name
	 * 			| The name of the instance this object is a representation of.
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * Return the ObjectRepresentations with accompanying list of DebuggerFields of the objects
	 * this ObjectRepresentation is a reference from.
	 * 
	 * @return	the ObjectRepresentations with accompanying list of DebuggerFields of the objects
	 * 			this ObjectRepresentation is a reference from.
	 */
	public HashMap<ObjectRepresentation, List<DebuggerField>> getReferences(){
		return fieldReferences;
	}

	/**
	 * Remove the null references of this ObjectRepresentation.
	 */
	public void removeNullReferences() {
		Iterator<ObjectRepresentation> it = fieldReferences.keySet().iterator();
		while(it.hasNext()){
			if (it.next() == null)
				it.remove();
		}
	}
	
}
